/*
    JavaScript autoComplete v1.0.4
    Copyright (c) 2014 Simon Steinberger / Pixabay
    GitHub: https://github.com/Pixabay/JavaScript-autoComplete
    License: http://www.opensource.org/licenses/mit-license.php
*/

var autoComplete = (function () {
  // "use strict";
  function autoComplete(options) {
    if (!document.querySelector) return;

    // helpers
    function hasClass(el, className) {
      return el.classList ? el.classList.contains(className) : new RegExp('\\b' + className + '\\b').test(el.className);
    }

    function addEvent(el, type, handler) {
      if (el.attachEvent) el.attachEvent('on' + type, handler); else el.addEventListener(type, handler);
    }

    function removeEvent(el, type, handler) {
      // if (el.removeEventListener) not working in IE11
      if (el.detachEvent) el.detachEvent('on' + type, handler); else el.removeEventListener(type, handler);
    }

    function live(elClass, event, cb, context) {
      addEvent(context || document, event, function (e) {
        var found, el = e.target || e.srcElement;
        while (el && !(found = hasClass(el, elClass))) el = el.parentElement;
        if (found) cb.call(el, e);
      });
    }

    var o = {
      selector: 0,
      source: 0,
      minChars: 3,
      delay: 150,
      offsetLeft: 0,
      offsetTop: 1,
      cache: 1,
      menuClass: '',
      renderItem: function (item, search) {
        // escape special characters
        search = search.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
        var re = new RegExp("(" + search.split(' ').join('|') + ")", "gi");
        return '<div class="autocomplete-suggestion" data-val="' + item + '">' + item.replace(re, "<b>$1</b>") + '</div>';
      },
      onSelect: function (e, term, item) {
      }
    };
    for (var k in options) {
      if (options.hasOwnProperty(k)) o[k] = options[k];
    }

    // init
    var elems = typeof o.selector == 'object' ? [o.selector] : document.querySelectorAll(o.selector);
    for (var i = 0; i < elems.length; i++) {
      var that = elems[i];

      // create suggestions container "sc"
      that.sc = document.createElement('div');
      that.sc.className = 'autocomplete-suggestions ' + o.menuClass;

      that.autocompleteAttr = that.getAttribute('autocomplete');
      that.setAttribute('autocomplete', 'off');
      that.cache = {};
      that.last_val = '';

      that.updateSC = function (resize, next) {
        var rect = that.getBoundingClientRect();
        that.sc.style.left = Math.round(rect.left + (window.pageXOffset || document.documentElement.scrollLeft) + o.offsetLeft) + 'px';
        that.sc.style.top = Math.round(rect.bottom + (window.pageYOffset || document.documentElement.scrollTop) + o.offsetTop) + 'px';
        that.sc.style.width = Math.round(rect.right - rect.left) + 'px'; // outerWidth
        if (!resize) {
          that.sc.style.display = 'block';
          if (!that.sc.maxHeight) {
            that.sc.maxHeight = parseInt((window.getComputedStyle ? getComputedStyle(that.sc, null) : that.sc.currentStyle).maxHeight);
          }
          if (!that.sc.suggestionHeight) that.sc.suggestionHeight = that.sc.querySelector('.autocomplete-suggestion').offsetHeight;
          if (that.sc.suggestionHeight)
            if (!next) that.sc.scrollTop = 0;
            else {
              var scrTop = that.sc.scrollTop,
                selTop = next.getBoundingClientRect().top - that.sc.getBoundingClientRect().top;
              if (selTop + that.sc.suggestionHeight - that.sc.maxHeight > 0)
                that.sc.scrollTop = selTop + that.sc.suggestionHeight + scrTop - that.sc.maxHeight;
              else if (selTop < 0)
                that.sc.scrollTop = selTop + scrTop;
            }
        }
      }
      addEvent(window, 'resize', that.updateSC);
      document.body.appendChild(that.sc);

      live('autocomplete-suggestion', 'mouseleave', function (e) {
        var sel = that.sc.querySelector('.autocomplete-suggestion.selected');
        if (sel) setTimeout(function () {
          sel.className = sel.className.replace('selected', '');
        }, 20);
      }, that.sc);

      live('autocomplete-suggestion', 'mouseover', function (e) {
        var sel = that.sc.querySelector('.autocomplete-suggestion.selected');
        if (sel) sel.className = sel.className.replace('selected', '');
        this.className += ' selected';
      }, that.sc);

      live('autocomplete-suggestion', 'mousedown', function (e) {
        if (hasClass(this, 'autocomplete-suggestion')) { // else outside click
          var v = this.getAttribute('data-val');
          that.value = v;
          o.onSelect(e, v, this);
          that.sc.style.display = 'none';
        }
      }, that.sc);

      that.blurHandler = function () {
        try {
          var over_sb = document.querySelector('.autocomplete-suggestions:hover');
        } catch (e) {
          var over_sb = 0;
        }
        if (!over_sb) {
          that.last_val = that.value;
          that.sc.style.display = 'none';
          setTimeout(function () {
            that.sc.style.display = 'none';
          }, 350); // hide suggestions on fast input
        } else if (that !== document.activeElement) setTimeout(function () {
          that.focus();
        }, 20);
      };
      addEvent(that, 'blur', that.blurHandler);

      var suggest = function (data) {
        var val = that.value;
        that.cache[val] = data;
        if (data.length && val.length >= o.minChars) {
          var s = '';
          for (var i = 0; i < data.length; i++) s += o.renderItem(data[i], val);
          that.sc.innerHTML = s;
          that.updateSC(0);
        }
        else
          that.sc.style.display = 'none';
      }

      that.keydownHandler = function (e) {
        var key = window.event ? e.keyCode : e.which;
        // down (40), up (38)
        if ((key == 40 || key == 38) && that.sc.innerHTML) {
          var next, sel = that.sc.querySelector('.autocomplete-suggestion.selected');
          if (!sel) {
            next = (key == 40) ? that.sc.querySelector('.autocomplete-suggestion') : that.sc.childNodes[that.sc.childNodes.length - 1]; // first : last
            next.className += ' selected';
            console.log(next);
            that.value = next.getAttribute('data-val');
          } else {
            next = (key == 40) ? sel.nextSibling : sel.previousSibling;
            if (next) {
              sel.className = sel.className.replace('selected', '');
              next.className += ' selected';
              that.value = next.getAttribute('data-val');
            }
            else {
              sel.className = sel.className.replace('selected', '');
              that.value = that.last_val;
              next = 0;
            }
          }
          that.updateSC(0, next);
          return false;
        }
        // esc
        else if (key == 27) {
          that.value = that.last_val;
          that.sc.style.display = 'none';
        }
        // enter
        else if (key == 13 || key == 9) {
          var sel = that.sc.querySelector('.autocomplete-suggestion.selected');
          if (sel && that.sc.style.display != 'none') {
            o.onSelect(e, sel.getAttribute('data-val'), sel);
            setTimeout(function () {
              that.sc.style.display = 'none';
            }, 20);
          }
        }
      };
      addEvent(that, 'keydown', that.keydownHandler);

      that.keyupHandler = function (e) {
        var key = window.event ? e.keyCode : e.which;
        if (!key || (key < 35 || key > 40) && key != 13 && key != 27) {
          var val = that.value;
          if (val.length >= o.minChars) {
            if (val != that.last_val) {
              that.last_val = val;
              clearTimeout(that.timer);
              if (o.cache) {
                if (val in that.cache) {
                  suggest(that.cache[val]);
                  return;
                }
                // no requests if previous suggestions were empty
                for (var i = 1; i < val.length - o.minChars; i++) {
                  var part = val.slice(0, val.length - i);
                  if (part in that.cache && !that.cache[part].length) {
                    suggest([]);
                    return;
                  }
                }
              }
              that.timer = setTimeout(function () {
                o.source(val, suggest)
              }, o.delay);
            }
          } else {
            that.last_val = val;
            that.sc.style.display = 'none';
          }
        }
      };
      addEvent(that, 'keyup', that.keyupHandler);

      that.focusHandler = function (e) {
        that.last_val = '\n';
        that.keyupHandler(e)
      };
      if (!o.minChars) addEvent(that, 'focus', that.focusHandler);
    }

    // public destroy method
    this.destroy = function () {
      for (var i = 0; i < elems.length; i++) {
        var that = elems[i];
        removeEvent(window, 'resize', that.updateSC);
        removeEvent(that, 'blur', that.blurHandler);
        removeEvent(that, 'focus', that.focusHandler);
        removeEvent(that, 'keydown', that.keydownHandler);
        removeEvent(that, 'keyup', that.keyupHandler);
        if (that.autocompleteAttr)
          that.setAttribute('autocomplete', that.autocompleteAttr);
        else
          that.removeAttribute('autocomplete');
        document.body.removeChild(that.sc);
        that = null;
      }
    };
  }

  return autoComplete;
})();

(function () {
  if (typeof define === 'function' && define.amd)
    define('autoComplete', function () {
      return autoComplete;
    });
  else if (typeof module !== 'undefined' && module.exports)
    module.exports = autoComplete;
  else
    window.autoComplete = autoComplete;
})();
